package com.haoma.service.impl;

import cn.hutool.core.date.DateUtil;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.haoma.elasticsearch.ArticleEs;
import com.haoma.elasticsearch.ArticleMapper;
import com.haoma.entity.Category;
import com.haoma.entity.Label;
import com.haoma.entity.Message;
import com.haoma.entity.Visitor;
import com.haoma.exception.BlogException;
import com.haoma.mapper.CategoryMapper;
import com.haoma.mapper.LabelMapper;
import com.haoma.mapper.VisitorMapper;
import com.haoma.service.ArticleService;
import com.haoma.vo.ClassifyVo;
import com.haoma.vo.HotArticleVo;
import com.haoma.vo.SearchVo;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightField;
import org.elasticsearch.search.sort.SortBuilders;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import java.io.IOException;
import java.util.*;

/**
 * Description:
 */
@Service
public class ArticleServiceImpl implements ArticleService {

    @Autowired
    private ArticleMapper articleMapper;

    @Autowired
    private CategoryMapper categoryMapper;

    @Autowired
    private LabelMapper labelMapper;

    private final Integer status = 1;

    @Autowired
    private RedisTemplate redisTemplate;

    /**
     * 根据id获取到文章详情
     *
     * @param id
     * @return
     */
    public ArticleEs getArticleDetail(Long id) {
        Optional<ArticleEs> optional = articleMapper.findById(id);
        if (optional.isEmpty()) {
            throw new BlogException("文章数据为空");
        }
        ArticleEs articleEs = optional.get();
        // 存入热度到redis不可能每次都直接更新Es热度达到10在更新
        // 每次点击存入redis并增量1 并返回到更新后的增量
        Double aDouble = redisTemplate
                .opsForZSet()
                .incrementScore("hot", "article" + id, 1);
        // 如果热度到达了10的余数就更新到es
        if (aDouble % 10 == 0) {
            articleEs.setHot(aDouble.intValue());
            articleMapper.save(articleEs);
        }
        return articleEs;
    }

    /**
     * 获取所有的分类
     *
     * @return
     */
    public List<Category> getCategoryList() {
        LambdaQueryWrapper<Category> categoryLambdaQueryWrapper = new LambdaQueryWrapper<>();
        categoryLambdaQueryWrapper.orderByDesc(Category::getCreateTime);
        return categoryMapper.selectList(categoryLambdaQueryWrapper);
    }

    /**
     * 获取前4条最火文章
     *
     * @return
     */
    public List<HotArticleVo> getHotArticle() {
        List<ArticleEs> hotArticleVos = articleMapper.findTop4ByStatusOrderByHotDescCreateTimeDesc(status);
        List<HotArticleVo> list = hotArticleVos.stream().map(articleEs -> {
            HotArticleVo hotArticleVo = new HotArticleVo();
            BeanUtils.copyProperties(articleEs, hotArticleVo);
            return hotArticleVo;
        }).toList();
        return list;
    }

    /**
     * 获取排序字段前5条文章
     *
     * @return
     */
    public List<HotArticleVo> getRecommendArticle() {
        List<ArticleEs> commendList = articleMapper.findTop5ByStatusOrderBySortAscCreateTimeDesc(status);
        List<HotArticleVo> list = commendList.stream().map(articleEs -> {
            HotArticleVo hotArticleVo = new HotArticleVo();
            BeanUtils.copyProperties(articleEs, hotArticleVo);
            return hotArticleVo;
        }).toList();
        return list;
    }

    /**
     * 文章分页查询
     *
     * @param page
     * @param limit
     * @return
     */
    public Page<ArticleEs> articlePage(Integer page, Integer limit) {
        // 获取当前日期
        Date date = new Date();
        String jsonFormat = DateUtil.format(date, "yyyy-MM-dd") + "visitor";
        // 查询redis是否有缓存
        Object userCount = redisTemplate.opsForValue().get(jsonFormat);
        if (userCount != null) {
            Integer count = Integer.parseInt((String) userCount);
            // 加1写入
            redisTemplate.opsForValue().set(jsonFormat, count + 1);
        } else {
            redisTemplate.opsForValue().set(jsonFormat, 1);
        }
        // 设置分页参数
        Pageable pageable = PageRequest.of(page - 1, limit);
        // 开始分页查询
        Page<ArticleEs> esPage = articleMapper.findByStatusOrderByCreateTimeDesc(status, pageable);
        return esPage;
    }

    /**
     * 获取文章所有的分类和该分类下前3的排序
     *
     * @return
     */
    public List<ClassifyVo> getClassifyList() {
        // 封装返回数据集合
        List<ClassifyVo> classifyVoList = new ArrayList<>();
        // 查询所有的分类
        List<Category> categoryList = this.getCategoryList();
        // 获取该分类下前3的数据
        for (Category category : categoryList) {
            // 封装返回对象
            ClassifyVo classifyVo = new ClassifyVo();
            classifyVo.setId(category.getId());
            classifyVo.setCount(category.getCount());
            classifyVo.setCategoryName(category.getName());
            // 查询
            List<ArticleEs> articleEsList = articleMapper.findTop3ByStatusAndCategoryNameOrderBySortAscCreateTimeDesc(status, category.getName());
            if (!CollectionUtils.isEmpty(articleEsList)) {
                List<HotArticleVo> list = articleEsList.stream().map(articleEs -> {
                    HotArticleVo hotArticleVo = new HotArticleVo();
                    BeanUtils.copyProperties(articleEs, hotArticleVo);
                    return hotArticleVo;
                }).toList();
                classifyVo.setHotArticleVoList(list);
            }
            classifyVoList.add(classifyVo);
        }
        return classifyVoList;
    }

    /**
     * 获取分类1标签2下的所有文章
     *
     * @param page
     * @param limit
     * @param type
     * @param name
     * @return
     */
    public List<HotArticleVo> getEntireList(Integer page, Integer limit, Integer type, String name) {
        // 设置分页参数
        PageRequest iPage = PageRequest.of(page - 1, limit);
        Page<ArticleEs> esPage;
        if (type == 1) {
            // 获取分类下的所有文章
            esPage = articleMapper.findByStatusAndCategoryNameOrderByCreateTimeDesc(status, name, iPage);
        } else {
            // 获取标签下的所有文章
            esPage = articleMapper.findByStatusAndLabelNameOrderByCreateTimeDesc(status, name, iPage);
        }
        List<HotArticleVo> list = esPage.stream().map(articleEs -> {
            HotArticleVo hotArticleVo = new HotArticleVo();
            hotArticleVo.setTotal(esPage.getTotalElements());
            BeanUtils.copyProperties(articleEs, hotArticleVo);
            return hotArticleVo;
        }).toList();

        return list;
    }

    /**
     * 获取所有的标签和统计图
     *
     * @return
     */
    public Map<String, List<Object>> getLabelList() {
        // 查询所有的标签名称
        LambdaQueryWrapper<Label> lambdaQueryWrapper = new LambdaQueryWrapper<>();
        lambdaQueryWrapper.orderByDesc(Label::getCreateTime);
        List<Label> labels = labelMapper.selectList(lambdaQueryWrapper);

        // 遍历到前10的标签
        List<Label> top10Label = labels.stream()
                .sorted(Comparator.comparing(Label::getLabelCount).reversed())
                .limit(10)
                .toList();

        // 存储数据表的数据
        List<String> labelNameList = new ArrayList<>();
        List<Integer> labelCountList = new ArrayList<>();

        for (Label label : top10Label) {
            labelNameList.add(label.getLabelName());
            labelCountList.add(label.getLabelCount());
        }

        Map<String, List<Object>> stringListMap = new HashMap<>();
        stringListMap.put("labelNameList", Collections.singletonList(labels));
        stringListMap.put("echartsNameList", Collections.singletonList(labelNameList));
        stringListMap.put("echartsCountList", Collections.singletonList(labelCountList));
        return stringListMap;
    }

    /**
     * 获取全部的文章和统计图
     *
     * @return
     */
    public Page<ArticleEs> getPigeonholeList(Integer page, Integer limit) {
        // 设置分页参数
        PageRequest of = PageRequest.of(page - 1, limit);
        // 开始查询
        Page<ArticleEs> byStatusOrderByCreateTimeDesc = articleMapper.findByStatusOrderByCreateTimeDesc(status, of);
        return byStatusOrderByCreateTimeDesc;
    }

    @Override
    public List<Integer> countPigeonhole() {
        // 存储每一个的发布文章的数量
        List<Integer> countList = new ArrayList<>();
        // 查询所有的文章
        List<ArticleEs> articleEsList = articleMapper.findByStatusOrderByCreateTimeDesc(status);
        // 遍历12个月份
        for (int i = 1; i <= 12; i++) {
            int count = 0;
            for (ArticleEs articleEs : articleEsList) {
                // 获取到月份
                Integer substring = Integer.valueOf(articleEs.getCreateTime().substring(6, 7));
                // 创建12个对象
                if (substring.equals(i)) {
                    count++;
                }
            }
            countList.add(count);
        }
        return countList;
    }

    @Autowired
    private MessageServiceImpl messageService;

    @Autowired
    private TalkServiceImpl talkService;

    @Autowired
    private VisitorMapper visitorMapper;

    @Autowired
    private MongoTemplate mongoTemplate;

    /**
     * 统计数量
     *
     * @return
     */
    public Map<String, Object> getCount() {
        // 统计文章数量
        long articleCount = articleMapper.count();
        // 统计留言数量
        List<Message> all = mongoTemplate.findAll(Message.class);
        // 统计说说数量
        long talkCount = talkService.count();
        // 统计网站访问量
        Object requestCount = redisTemplate.opsForValue().get("request-count");
        Long aLong = Long.valueOf(requestCount.toString());
        // 统计访客
        List<Visitor> visitors = visitorMapper.selectList(null);
        long sum = visitors.stream().mapToLong(Visitor::getCount).sum();
        Map<String, Object> map = new HashMap();
        map.put("talkCount", talkCount);
        map.put("messageCount", all.size());
        map.put("articleCount", articleCount);
        map.put("requestCount", aLong);
        map.put("userCount", sum);
        Page<ArticleEs> articleEs = articleMapper.findByOrderByUpdateTimeDesc(PageRequest.of(0, 1));
        map.put("update", articleEs.getContent().get(0).getUpdateTime());
        return map;
    }

    @Autowired
    private RestHighLevelClient restHighLevelClient;

    /**
     * 搜索功能
     *
     * @return
     */
    public List<SearchVo> search(String value) throws IOException {
        // 绑定索引
        SearchRequest searchRequest = new SearchRequest("article");

        // 高亮
        HighlightBuilder highlightBuilder = new HighlightBuilder();
        highlightBuilder.field("title");
        highlightBuilder.requireFieldMatch(false);
        highlightBuilder.preTags("<span style='color:#85cdff'>");
        highlightBuilder.postTags("</span>");

        // 构建搜索条件
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder()
                // 查询title
                .query(QueryBuilders.matchQuery("title", value))
                // 排序
                .sort(SortBuilders.fieldSort("hot").order(SortOrder.DESC))
                .sort(SortBuilders.fieldSort("createTime").order(SortOrder.DESC))
                .from(0)// 指定从哪条开始查询
                .size(5)// 需要查出的总记录条数
                .highlighter(highlightBuilder); // 高亮

        searchRequest.source(searchSourceBuilder);
        // 开始搜索
        SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);


        // 得到返回的所有查到的数据
        SearchHit[] searchHits = searchResponse.getHits().getHits();

        // 封装返回的数据
        List<SearchVo> searchVoList = new ArrayList<>();

        // 遍历封装
        for (org.elasticsearch.search.SearchHit hit : searchHits) {
            // 创建返回数据
            SearchVo searchVo = new SearchVo();
            // 将对象转换为文章对象
            ArticleEs articleEs = JSONUtil.toBean(hit.getSourceAsString(), ArticleEs.class);
            // 填充标题
            searchVo.setTitle(articleEs.getTitle());
            // 填充链接
            searchVo.setLinkId(articleEs.getId());
            // 获取高亮字段
            HighlightField highlightField = hit.getHighlightFields().get("title");
            // 判断是否有高亮字段
            if (highlightField != null) {
                searchVo.setValue(highlightField.getFragments()[0].toString());
            }
            searchVoList.add(searchVo);
        }
        return searchVoList;
    }
}
